#include <iostream>
#include <string>
#include <cstring>
#include <unistd.h>
#include <stdlib.h>
#include <ctype.h>
#include <unordered_map>
#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include "log.hpp"


static void Usage(const std::string porc)
{
    std::cout << "Usage:\n\t" << porc << " port [ip]" << std::endl;
}

class Udpserver
{
public:
    Udpserver(int port,std::string ip="")
        :port_((uint16_t)port),ip_(ip),sockfd_(-1)
    {

    }
    ~Udpserver(){

    }
    //初始化接口
    void init(){
        sockfd_=socket(AF_INET,SOCK_DGRAM,0);
        if(sockfd_<0){
            logMessage(FATAL, "socket:%s:%d", strerror(errno), sockfd_);
            exit(1);
        }
        logMessage(DEBUG,"create socket sucess: %d",sockfd_);
        //绑定端口
        struct sockaddr_in local;
        bzero(&local,sizeof(local));
        local.sin_family=AF_INET;
        local.sin_port=htons(port_);
        // 服务器都必须具有IP地址,"xx.yy.zz.aaa",字符串风格点分十进制 -> 4字节IP -> uint32_t ip
        // INADDR_ANY(0): 程序员不关心会bind到哪一个ip， 任意地址bind，强烈推荐的做法，所有服务器一般的做法
        // inet_addr: 指定填充确定的IP，特殊用途，或者测试时使用，除了做转化，还会自动给我们进行 h—>n
        //绑定任意端口
        local.sin_addr.s_addr=ip_.empty()?htonl(INADDR_ANY):inet_addr(ip_.c_str());
        int ret=bind(sockfd_,(const struct sockaddr*)&local,sizeof(local));
        if(ret<0){
            logMessage(FATAL,"bind:%s:%d",strerror(errno),sockfd_);
            exit(2);
        }
        logMessage(DEBUG,"bind sucess:%d",sockfd_);
    }



    void checkOnlineUser(std::string& clientip,uint32_t clientport,struct sockaddr_in& client){
        std::string key = clientip;
        key += ":";
        key += std::to_string(clientport);
        auto iter=users.find(key);
        if(iter==users.end()){
            users.insert({key, client});
        }
        else{

            //do nothing
        }
    }
    //接收消息，实现广播
    void start(){
        //发送缓存和接收缓存
        char recvbuff[1024]={0};
        char sendbuff[1024]={0};
        while(true){
            struct sockaddr_in client;
            socklen_t len=sizeof(client);
            ssize_t s=recvfrom(sockfd_,recvbuff,sizeof(recvbuff)-1,0,(struct sockaddr*)&client,&len);
            if(s>0){
                recvbuff[s]=0;
            }
            else if(s==-1)
            {
                logMessage(WARINING,"recvform:%s:%d",strerror(errno),sockfd_);
                continue;
            }
            // 读取成功的,除了读取到对方的数据，还要读取到对方的网络地址[ip:port]
            std::string clientIp = inet_ntoa(client.sin_addr);       //拿到了对方的IP
            uint32_t clientPort = ntohs(client.sin_port); // 拿到了对方的port

            checkOnlineUser(clientIp, clientPort, client); //如果存在，什么都不做，如果不存在，就添加
            // 打印出来客户端给服务器发送过来的消息
            logMessage(NOTICE, "[%s:%d]# %s", clientIp.c_str(), clientPort, recvbuff);

            //实现广播
            messageRoute(clientIp,clientPort,recvbuff);
        }
    }
    void messageRoute(std::string ip, uint32_t port, std::string info)
    {

        std::string message = "[";
        message += ip;
        message += ":";
        message += std::to_string(port);
        message += "]# ";
        message += info;
        for(auto &user : users)
        {
            sendto(sockfd_, message.c_str(), message.size(), 0, (struct sockaddr*)&(user.second), sizeof(user.second));
        }
        
    }
private:
    int sockfd_; //套接字
    uint16_t port_;
    std::string ip_;
    std::unordered_map<std::string, struct sockaddr_in> users; //记录在线用户
};



int main(int argc,char*argv[]){
    if(argc!=2&&argc!=3){
        Usage(argv[0]);
        exit(3);
    }
    uint16_t port=atoi(argv[1]);
    std::string ip;
    if(argc==3){
        ip=argv[2];
    }
    Udpserver svr(port,ip);
    svr.init();
    svr.start();
    return 0;
}